import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useParams } from "react-router-dom";

import "@/styles/file/file.scss";
import { Skeleton, Button, Breadcrumb } from "antd";
import { HomeOutlined, UploadOutlined, MenuOutlined, AppstoreOutlined } from "@ant-design/icons";

import { FileIconLazyList } from "@/components/file/fileIcon";
import { FileUpload, FileUploadList } from "@/components/file/upload";
import { getInfo } from "@/api/file";
import { fileThreadChunk, statusEnum } from "@/utils/file";
import { handleBatchTask } from "@/utils/file/chunkUtils";
import { useThrottle, useArrayState, useInterval } from "@/hooks";
import { message } from "antd";
import { byteFormate } from "@/utils/formate";
import { fileSliceUploadCheck, fileSliceUpload, fileSliceMerge, fileSliceMergeVerify } from "@/api/file";

const handleBreadcrumb = (breadcrumbs = []) => {
  return breadcrumbs.map((v, index) => {
    // 首字母大写，其余小写
    const title = v.title.slice(0, 1).toUpperCase() + v.title.slice(1).toLocaleLowerCase();
    if (v.href === "" || v.href === "/") {
      const homeTitle = (
        <>
          <HomeOutlined />
          <span>{title}</span>
        </>
      );
      return { ...v, title: homeTitle, href: "/file" };
    }

    if (index === breadcrumbs.length - 1) {
      return { ...v, title, href: undefined };
    }

    return { ...v, title, href: "/file" + v.href };
  });
};

const sendUpload = (props) => {
  return new Promise((resolve) => {
    let { data = [], handle, callback, onUploadProgress } = props;
    const result = [];
    const nextRequest = async (index = 0) => {
      const res = handle(data[index]); // 处理每个切片
      result[index] = await fileSliceUpload(res, { onUploadProgress });

      callback && callback(res);
      return ++index >= data.length ? resolve(result) : nextRequest(index);
    };

    nextRequest();
  });
};

const HeaderActionComponent = ({ breadcrumbs = [], previewView, onPreviewChange }) => {
  const { "*": path } = useParams();
  const breadcrumbItems = useMemo(() => handleBreadcrumb(breadcrumbs), [breadcrumbs]);
  const [uploadList, setUploadList] = useArrayState();
  const [messageApi, contextHolder] = message.useMessage();

  const onThrottleChange = useThrottle(setUploadList, 200);
  const handleMergeVerify = useInterval(fileSliceMergeVerify, 1000);

  const handleUploadTask = async (fileInfo, index) => {
    let { breakPointIndex = -1, uploadedSize = 0 } = await fileSliceUploadCheck(fileInfo.dir);
    const result = { ...fileInfo, speed: "", uploadProgress: 0, sliceProgress: 0, status: statusEnum.LOADING };

    const updateStatus = (status, autoUpload = true) => {
      result.status = status || result.status;
      result.statusName = statusEnum.getDesc(status || result.status);
      autoUpload && setUploadList({ value: result, index });
    };

    // 分片进度
    let sliceProgress = 0;
    const onSliceProgress = (progressCount) => {
      sliceProgress++;
      const progress = Math.floor((sliceProgress / progressCount) * 100);
      if (result.sliceProgress !== progress) {
        result.sliceProgress = progress;
        setUploadList({ value: result, index });
      }
    };

    // 上传进度
    let uploadProgress = uploadedSize;
    const onUploadProgress = ({ endIndex, startIndex }) => {
      uploadProgress += endIndex - startIndex;
      const progress = Math.floor((uploadProgress / fileInfo.size) * 100);
      result.uploadProgress = progress;
      updateStatus(statusEnum.UPLOADING, false);
      onThrottleChange({ value: result, index });
    };

    // 上传速度
    const onSpeed = ({ loaded, costTime }) => {
      const speedStr = byteFormate(loaded);
      let [speed, unit] = speedStr.split(" ");
      speed = (speed / 8 / costTime) * 1000;
      result.speed = `${Math.floor(speed)} ${unit}/s`;
      onThrottleChange({ value: result, index });
    };

    const handleBatchTaskAction = ({ file, start, batchCount }) => {
      return new Promise(async (resolve) => {
        const startIndex = breakPointIndex + 1;
        const onProgress = (_, v) => onSliceProgress(v * batchCount); // 分片进度

        const data = await fileThreadChunk({ file, uploadedSize, startIndex, onProgress });

        if (data.length) {
          breakPointIndex += data.length;
          // 参数处理
          data.forEach((values) => {
            values.startIndex += start;
            values.endIndex += start;
            values.index += startIndex;
          });

          // 上传分片
          await sendUpload({
            data,
            handle: (item) => ({ ...item, dir: fileInfo.dir }),
            callback: onUploadProgress,
            onUploadProgress: onSpeed,
          });
        }
        result.speed = "";
        updateStatus(statusEnum.LOADING);
        return resolve(data);
      });
    };

    updateStatus(statusEnum.LOADING);
    await handleBatchTask({ file: fileInfo.file, uploadedSize }, handleBatchTaskAction);
    result.speed = "";
    updateStatus(statusEnum.COMBINED);

    // 合并分片
    const savePath = path ? `/${path}/${fileInfo.name}` : `/${fileInfo.name}`;
    const mergeData = { dir: fileInfo.dir, fileSize: fileInfo.size, filePath: savePath };
    const tackId = await fileSliceMerge(mergeData);
    //  合并校验
    await handleMergeVerify(tackId);
    updateStatus(statusEnum.SUCCESS, false);
    result.statusName = "";
    setUploadList({ value: result, index });
  };

  const onAddUpload = (value) => {
    if (uploadList.find((item) => item.hash === value.hash)) {
      return messageApi.open({ type: "error", content: "文件上传任务已存在" });
    }
    value.dir = path ? `/${path}/${value.hash}` : `/${value.hash}`;
    setUploadList({ value, index: uploadList.length });
    handleUploadTask(value, uploadList.length);
  };

  return (
    <>
      {contextHolder}
      <div className="header-action-card">
        <div className="header-action">
          <Breadcrumb separator=">" items={breadcrumbItems} />
        </div>
        <div className="header-action header-action-right">
          <FileUploadList items={uploadList} />
          <FileUpload type="text" icon={<UploadOutlined />} callback={onAddUpload} />
          <Button type="text" icon={previewView ? <MenuOutlined /> : <AppstoreOutlined />} onClick={onPreviewChange} />
        </div>
      </div>
    </>
  );
};

const FileComponent = () => {
  const { "*": path } = useParams();
  const [dataSource, setDataSource] = useState({ fileList: [] });
  const [loading, setLoading] = useState(false);
  const [previewView, setPreviewView] = useState(false);

  const initData = useCallback(() => {
    setLoading(true);
    getInfo(path)
      .then((res) => setDataSource(res))
      .finally(() => setLoading(false));
  }, [path]);

  useEffect(() => initData(), [initData]);

  const handleCallback = (file, index) => {
    const items = [...dataSource.fileList];
    items[index] = file;
    items.sort((a, b) => a.fileName.localeCompare(b.fileName));
    setDataSource({ ...dataSource, fileList: items });
  };

  return (
    <div className="file-warrper">
      <HeaderActionComponent
        breadcrumbs={dataSource.breadcrumbs}
        previewView={previewView}
        onPreviewChange={() => setPreviewView(!previewView)}
      />
      <div className="file-container">
        {loading && <Skeleton className="spin-card" active />}
        <FileIconLazyList items={dataSource.fileList} onCallback={handleCallback} />
      </div>
    </div>
  );
};

export default FileComponent;
